﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Markup;
using Microsoft.Build.Evaluation;

namespace XamlPadHelpers
{
    [ContentProperty("AssemblyReferences")]
    public class ProjectData
    {
        static ProjectData()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
        }
        public ProjectData()
        {
        }
        public ProjectData(string fileName)
        {
            //DirectoryInfo dirInfo = new DirectoryInfo(System.Environment.CurrentDirectory);
            //FileInfo[] files = dirInfo.GetFiles(fileName);
            //if (files.Length == 1 && files[0].Exists)
            //{
            //    fileName = files[0].FullName;
            //}
            FileName = fileName;
            LookupProjectData();
        }
        public string FileName
        {
            set
            {
                _fileName = value;
                NotifyPropertyChanged("FileName");
            }
            get { return _fileName; }
        }

        public string TargetFrameworkIdentifier { get; set; }

        [DefaultValue(null)]
        public string ApplicationDefinition { get; set; }

        private List<string> _XamlFiles;
        public List<string> XamlFiles
        {
            get
            {
                if (_XamlFiles == null)
                {
                    _XamlFiles = new List<string>();
                }
                return _XamlFiles;
            }
        }

        [TypeConverter(typeof(NameReferenceConverter))]
        public AssemblyData LocalAssembly { get; set; }

        public List<AssemblyData> AssemblyReferences
        {
            get
            {
                if (_assemblyReferences == null)
                    _assemblyReferences = new List<AssemblyData>();
                return _assemblyReferences;
            }
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion

        private string _fileName;
        private List<AssemblyData> _assemblyReferences;
        private FileInfo _projectFileInfo;

        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            foreach (Assembly assem in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assem.FullName == args.Name)
                {
                    return assem;
                }
            }
            string silverlightInstallPath = @"C:\Program Files\Microsoft Silverlight\4.0.50204.0";
            AssemblyName assemName = new AssemblyName(args.Name);
            string fileName = silverlightInstallPath + "\\" + assemName.Name + ".dll";
            Assembly assembly = null;
            if (!File.Exists(fileName))
            {
                fileName = @"C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client" + "\\" + assemName.Name + ".dll";
            }
            if (File.Exists(fileName))
            {
                assembly = Assembly.LoadFrom(fileName);
            }
            return assembly;
        }

        public FileInfo GetFileInfoFromRelativePath(string relativePath)
        {
            if (_projectFileInfo == null)
                _projectFileInfo = new FileInfo(FileName);
            if (relativePath.Contains(':'))
            {
                return new FileInfo(relativePath);
            }
            else
            {
                return new FileInfo(_projectFileInfo.Directory.FullName + "\\" + relativePath);
            }

        }

        public void LookupProjectData()
        {
            DirectoryInfo silverlightReferenceAssembliesDirectory = new DirectoryInfo(@"C:\Program Files\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0");
            DirectoryInfo silverlightSDKDirectory = new DirectoryInfo(@"C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client");
            DirectoryInfo dotnetReferenceAssembliesDirectory = new DirectoryInfo(@"C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0");

            ProjectCollection projectCollection = new ProjectCollection();
            Project project = projectCollection.LoadProject(FileName);
            IEnumerable<ProjectItem> items = project.AllEvaluatedItems;
            TargetFrameworkIdentifier = project.GetPropertyValue("TargetFrameworkIdentifier");

            //List<string> xamlItems = new List<string>();
            List<string> projectReferences = new List<string>();
            foreach (ProjectItem item in items)
            {
                AssemblyData assemblyData;
                string include = item.EvaluatedInclude;
                switch (item.ItemType)
                {
                    case "BuiltProjectOutputGroupKeyOutput":
                        //Use Assembly.LoadFile when you want to load them...
                        assemblyData = new AssemblyData() { AssemblyPath = include };
                        AssemblyReferences.Add(assemblyData);
                        break;
                    case "Reference":
                        string hintPath = item.GetMetadataValue("HintPath");
                        if (!string.IsNullOrEmpty(hintPath))
                        {
                            FileInfo refAssembly = GetFileInfoFromRelativePath(hintPath);
                            assemblyData = new AssemblyData() { AssemblyPath = refAssembly.FullName };
                            AssemblyReferences.Add(assemblyData);
                        }
                        else
                        {
                            if (TargetFrameworkIdentifier == "Silverlight")
                            {
                                int comma = include.IndexOf(',');
                                if (comma > -1)
                                {
                                    include = include.Substring(0, comma);
                                }
                                FileInfo[] refAssembly = silverlightReferenceAssembliesDirectory.GetFiles(include + ".dll");
                                if (refAssembly.Length == 0)
                                {
                                    refAssembly = silverlightSDKDirectory.GetFiles(include + ".dll");
                                }
                                if (refAssembly.Length > 0)
                                {
                                    assemblyData = new AssemblyData() { AssemblyPath = refAssembly[0].FullName };
                                    AssemblyReferences.Add(assemblyData);
                                }
                                else
                                {

                                }
                            }
                            else
                            {
                                int comma = include.IndexOf(',');
                                if (comma > -1)
                                {
                                    include = include.Substring(0, comma);
                                }
                                FileInfo[] refAssembly = dotnetReferenceAssembliesDirectory.GetFiles(include + ".dll");
                                if (refAssembly.Length > 0)
                                {
                                    assemblyData = new AssemblyData() { AssemblyPath = refAssembly[0].FullName };
                                    AssemblyReferences.Add(assemblyData);
                                }
                                else
                                {

                                }
                            }
                        }
                        break;
                    case "ProjectReference":
                        try
                        {
                            FileInfo projectReferenceFile = GetFileInfoFromRelativePath(include);
                            string projectReferenceAssembly = DetermineAssemblyPathFromProject(projectReferenceFile, projectCollection);
                            assemblyData = new AssemblyData() { AssemblyPath = projectReferenceAssembly };
                            AssemblyReferences.Add(assemblyData);
                        }
                        catch (Exception ex)
                        {
                            Exception newEx = new Exception("Could not load assembly: " + include, ex);
                            //TODO: projectData.Exceptions.Add(newEx);
                        }
                        break;
                    case "ApplicationDefinition":
                    case "Page":
                        if (item.ItemType == "ApplicationDefinition")
                        {
                            if (ApplicationDefinition != null)
                            {
                                throw new Exception("Project files should not have more than 1 application definition file.");
                            }
                            ApplicationDefinition = include;
                        }
                        XamlFiles.Add(include);

                        break;
                    default:
                        break;
                }
            }

            LocalAssembly = AssemblyReferences.Count > 0 ? AssemblyReferences[AssemblyReferences.Count - 1] : null;
        }

        private static string DetermineAssemblyPathFromProject(FileInfo fileInfo, ProjectCollection projectCollection)
        {
            Project project = projectCollection.LoadProject(fileInfo.FullName);
            IEnumerable<ProjectItem> items = project.AllEvaluatedItems;
            foreach (ProjectItem item in items)
            {
                if (item.ItemType == "BuiltProjectOutputGroupKeyOutput")
                    return item.EvaluatedInclude;
            }
            return null;
        }

        //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        //public ResourceDictionary ApplicationResources
        //{
        //    get
        //    {
        //        if (_applicationResources == null && ApplicationDefinition != null)
        //        {
        //            string appFileName = this.GetFileInfoFromRelativePath(ApplicationDefinition).FullName;
        //            XamlDocument appDefinitionDocument = new XamlDocument()
        //            {
        //                FileName = appFileName,
        //                XamlString = File.ReadAllText(appFileName),
        //                Project = this
        //            };

        //            var proxyLookup = new Dictionary<XamlType, XamlType>();
        //            proxyLookup.Add(XamlPad.SchemaContext.GetXamlType(typeof(Application)), XamlPad.SchemaContext.GetXamlType(typeof(ApplicationProxy)));
        //            //TODO: consider caching the app.xaml so this isn't wasted work.
        //            object result = XamlEngine.LoadXamlDocument(appDefinitionDocument, XamlPad.SchemaContext, proxyLookup, appDefinitionDocument.Project.LocalAssembly);
        //            ApplicationProxy appProxy = result as ApplicationProxy;
        //            if (appProxy != null)
        //            {
        //                _applicationResources = appProxy.Resources;
        //            }
        //            else
        //            {

        //            }

        //        }
        //        return _applicationResources;
        //    }
        //    set
        //    {
        //        _applicationResources = value;
        //    }
        //}
        //private ResourceDictionary _applicationResources;
    }
}
